﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using RPGProject.Data_Structures;
namespace RPGProject.GameWorld.GameObjects
{
    class Enemy : Character
    {
        /// <summary>
        /// A random for the whole class to use.
        /// </summary>
        private static Random rand = new Random();
        public Enemy(int x, int y, String name, World world)
            : base(x, y, Constants.GetInstance()["Ninja.MaxHP"], 'N', ConsoleColor.Cyan, name, world, "enemy")
        {
        }
        /// <summary>
        /// Generic Listen method with specifics to the Enemy subclass.
        /// </summary>
        /// <param name="e">The event to look at.</param>
        /// <returns></returns>
        public override GameObjectEvent Listen(GameObjectEvent e)
        {
            GameObjectEvent ret;
            // these variables are the multipliers that affect the ninja's
            // based on how many ninjas are on the field
            // at 3 damage taken and given is standardized
            double toEnemy = (double)(int)this.world["enemycount"] / (double)Constants.GetInstance()["Ninja.StandardAmount"];
            double toPlayer = 1 / toEnemy;
            if (e.Name == "attackAttempt")
            {
                ret = new GameObjectEvent("kill");
                ret["target"] = this;
                ret["source"] = e["source"];
                this.world.Listen(ret);
                return null;
            }
            else if (e.Name == "enemyturn")
            {
                // what the enemy does on his turn
                Player player = (Player)this.world["player"];
                int xdiff;
                int ydiff;
                int xdir;
                int ydir;
                int xmod;
                int ymod;
                int range;
                int dist;
                Vector<World.AStarNode> temp = this.world.FindPath(this, (int)player["x"], (int)player["y"]);
                // goes through three action points

                // range of shuriken
                range = (int)((double)Constants.GetInstance()["Ninja.Range"] * toPlayer);
                // distances and directions
                xdiff = (int)this["x"] - (int)player["x"];
                ydiff = (int)this["y"] - (int)player["y"];
                xdir = Math.Sign(xdiff);
                ydir = Math.Sign(ydiff);
                dist = (int)Math.Sqrt(xdiff * xdiff + ydiff * ydiff);
                // if the enemy hasn't shot and is in range
                // attempt to shoot
                if ((bool)e["last"] && dist <= range)
                {
                    // if the enemy is in a directional path from the player shoot
                    if (xdiff == 0 || ydiff == 0 || Math.Abs(xdiff) == Math.Abs(ydiff))
                    {
                        Projectile proj = new Projectile((int)this["x"], (int)this["y"], '+', -xdir, -ydir, (int)((double)Constants.GetInstance()["Ninja.RangedDamage"] * toPlayer), range, "shuriken", this, this.world);
                        GameObjectEvent projRet = new GameObjectEvent("projectile");
                        projRet["source"] = proj;
                        this["hasshot"] = true;
                        return projRet;
                    }
                }
                // otherwise attempt to move
                ret = this.GetMoveEvent();
                ret["source"] = this;
                // moves closer on a 4/5s chance
                // 1/5 chance to stay still
                xmod = Enemy.rand.Next(-4, 1) * xdir;
                ymod = Enemy.rand.Next(-4, 1) * ydir;
                if (temp != null && temp.Count > 0)
                {
                    World.AStarNode test = temp.Pop();
                    xmod = test.x - (int)this["x"];
                    ymod = test.y - (int)this["y"];
                }

                ret["xmod"] = Math.Sign(xmod);
                ret["ymod"] = Math.Sign(ymod);
                if ((int)ret["xmod"] != (int)ret["ymod"] || (int)ret["xmod"] != 0)
                    this.world.Listen(ret);

            }
            else if (e.Name == "damage" && e.Has("target") && e["target"] == this)
            {
                // process damage
                this["hp"] = (int)this["hp"] - (int)((double)(int)e["damage"] * toEnemy);
                this.InvariantHealth((GameObject)e["source"]);
            }
            else if (e.Name == "collision" && e.Has("source") && e["source"] == this)
            {
                // if this is the source of the collision to a GameObject
                // attempts to attack it.
                GameObject target = (GameObject)e["target"];
                if (target is Player)
                {
                    GameObjectEvent ge = this.GetDamageEvent();
                    ge["target"] = target;
                    ge["damage"] = (int)((double)Constants.GetInstance()["Ninja.MeleeDamage"] * toPlayer);
                    this.world.Listen(ge);
                    return null;
                }
            }
            else if (e.Name == "enemyreset")
            {
                this.InvariantHealth(null);
            }
            else
            {
                return base.Listen(e);
            }
            return null;
        }
        public override void InvariantHealth(GameObject source)
        {
            int hp = (int)this["hp"];
            if(hp >= 80)
            {
                this.iconColor = ConsoleColor.Cyan;
            } 
            else if(hp >= 60)
            {
                this.iconColor = ConsoleColor.DarkCyan;
            } 
            else if(hp >= 40)
            {
                this.iconColor = ConsoleColor.Blue;
            }
            else if (hp >= 20)
            {
                this.iconColor = ConsoleColor.DarkBlue;
            }
            else
            {
                this.iconColor = ConsoleColor.Red;
            }
            base.InvariantHealth(source);
        }
    }
}
